from getopt import getopt
import re
import sys

from encoding_parser import parse_files

def gen_dispatcher(unit, f):
    def translate_enc_list(enc_list):
        else_str = '    '
        for enc in enc_list:
            bitpat, name = enc[:2]
            p1 = bitpat.translate(str.maketrans('x0', '01'))
            p2 = bitpat.translate(str.maketrans('x', '0'))
            f.write('        %s if ((instr & 0x%08x) == 0x%08x) exec_%s(instr);\n' % (else_str, int(p1, 2), int(p2, 2), name))
            else_str = 'else'
        f.write('        %s unimplemented();\n' % else_str)
    encoding_list, directive_set = unit
    fn_name = directive_set['fn_name']
    sd = int(directive_set.get('switch_depth', '0'))
    f.write('void %s(uint32_t instr)\n' % fn_name)
    f.write('{\n')
    if sd > 0:
        case = [[] for _ in range(1<<sd)]
        for bitpat, name, line_num in encoding_list:
            opcode = int(bitpat[:sd], 2)
            case[opcode].append((bitpat[sd:], name))
        f.write('    switch (instr>>%d&%d) {\n' % (28-sd, (1<<sd)-1))
        for opcode, enc_list in enumerate(case):
            f.write('    case %d:\n' % opcode)
            translate_enc_list(enc_list)
            f.write('        break;\n')
        f.write('    }\n')
    else:
        translate_enc_list(encoding_list)
    f.write('}\n')

def gen_forward_decl(unit, f):
    encoding_list = unit[0]
    for enc in encoding_list:
        name = enc[1]
        f.write('void exec_%s(uint32_t instr);\n' % name)

def usage():
    print('usage: ...', file=sys.stderr)
    exit(2)

def main():
    opt_list, args = getopt(sys.argv[1:], 'o:')
    outfilepath = ''
    for key, val in opt_list:
        if key == '-o':
            outfilepath = val
    unit_list = parse_files(args)
    if unit_list is None:
        exit(1)
    if outfilepath != '':
        with open(outfilepath, 'w') as f:
            f.write('#include <stdint.h>\n')
            f.write('void unimplemented(void);\n')
            for unit in unit_list:
                gen_forward_decl(unit, f)
            for unit in unit_list:
                gen_dispatcher(unit, f)

if __name__ == "__main__":
    main()
